
#import "BWBorkStitchDocument.h"

#import "BWStitchView.h"
#import "BWGrid.h"
#import "BWEvenweave.h"
#import "BWStitch.h"
#import "BWDocumentUndo.h"
#import "BWStitchArray.h"
#import "BWStitchSelection.h"
#import "BWConfigTemplate.h"

#import "BWInspector.h"
#import "BWInspectorPane.h"
#import "BWFabricInspectorPane.h"
#import "BWLabelInspectorPane.h"
#import "BWStitchInspectorPane.h"
#import "BWShiftInspectorPane.h"

NSString *BWBorkStitchDocument_StitchSelectionChangeNotification
    = @"BWBorkStitchDocument stitch selection change notification";

NSString *BWBorkStitchDocument_DocumentActivateNotification
    = @"BWBorkStitchDocument document activate notification";
NSString *BWBorkStitchDocument_DocumentDeactivateNotification
    = @"BWBorkStitchDocument document deactivate notification";

@implementation BWBorkStitchDocument

- (BWStitch *) makeTemplateStitch
{
    BWStitch *stitch = [[BWStitch alloc] init];

    BWGrid_Square square;
    BWGrid_SquarePart part;

    square.x = square.y = 1;
    part = BWGrid_Center;

    [stitch setStartSquare: square  part: part];

    square.x = 4;
    [stitch setEndSquare: square  part: part];

    [stitch setWidth: 10.0];
    [stitch setFillColor: [NSColor whiteColor]];

    [stitch autorelease];

    return (stitch);

} // makeTemplateStitch


- (id) init
{
    srandom (time(NULL));

    if (self = [super init]) {
        grid = [[BWEvenweave alloc] init];
        stitches = [[BWStitchArray alloc] init];
        templateStitches = [[NSMutableArray alloc] init];

        NSFontManager *fontManager;
        fontManager = [NSFontManager sharedFontManager];
        
        labelFont = [NSFont fontWithName: @"Helvetica"  size: 22];

        labelFont = [fontManager convertFont: labelFont
                                 toHaveTrait: NSBoldFontMask];

        printAreaAnchor1.x = BWGrid_NO_SQUARE;
        printAreaAnchor1.y = BWGrid_NO_SQUARE;
        printAreaAnchor2.x = BWGrid_NO_SQUARE;
        printAreaAnchor2.y = BWGrid_NO_SQUARE;

        BWStitch *templateStitch = [self makeTemplateStitch];

        [templateStitches addObject: templateStitch];
        selectedStitches = [[BWStitchSelection  alloc] init];
        [selectedStitches setStitches: stitches];

        trackCenterOnly = YES;

        [BWConfigTemplate setApplicationName: @"BorkStitch"];
    }

    return (self);

} // init


- (void) dealloc
{
    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];

    [center removeObserver: self];
    [stitchView setDelegate: nil];

    [selectedStitches release];

    [labelFont release];
    [grid release];
    [stitches release];

    [undoQueue release];
    [notificationValue release];

    [super dealloc];

} // dealloc



- (NSWindow *) window
{
    NSArray *windowControllers;
    windowControllers = [self windowControllers];

    NSWindowController *controller;
    controller = [windowControllers objectAtIndex: 0];

    NSWindow *window;
    window = [controller window];

    return (window);

} // window


- (NSWindowController *) windowController
{
    NSArray *windowControllers;
    windowControllers = [self windowControllers];

    NSWindowController *controller;
    controller = [windowControllers objectAtIndex: 0];

    return (controller);

} // windowController


- (NSString *) windowNibName
{
    return (@"BWBorkStitchDocument");
} // windowNibName


- (void) windowControllerDidLoadNib: (NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];

    [stitchView setGrid: grid];
    [stitchView setStitches: stitches];
    [stitchView setTemplateStitch: [templateStitches objectAtIndex: 0]];
    [stitchView setShowBehindStitches: showBehindStitches];
    [stitchView setTrackCenterOnly: trackCenterOnly];
    [stitchView setSelectedStitchSet: selectedStitches];

    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];

    [center addObserver: self
            selector: @selector(newStitchNotification:)
            name: BWStitchView_NewStitchNotification
            object: stitchView];

    [center addObserver: self
            selector: @selector(stitchSelectedNotification:)
            name: BWStitchView_StitchSelectedNotification
            object: stitchView];

    [center addObserver: self
            selector: @selector(stitchUnselectedNotification:)
            name: BWStitchView_StitchUnselectedNotification
            object: stitchView];

    [center addObserver: self
            selector: @selector(printAreaChangeNotification:)
            name: BWStitchView_PrintAreaChangeNotification
            object: stitchView];

    undoQueue = [[NSNotificationQueue alloc]
                    initWithNotificationCenter: center];
    [self setupFabricUndo];
    [self setupStitchUndo];


    [stitchView setLabelFont: labelFont];

    [labelFont retain];

    [stitchView setPrintAreaAnchor1: printAreaAnchor1
                anchor2: printAreaAnchor2];

    [stitchView setShowBehindStitches: showBehindStitches];
    [showBehindStitchesCheckbox
        setState: (showBehindStitches) ? NSOnState : NSOffState];

    [stitchView setDelegate: self];

    if (loadedWindowBounds.size.width > 0) {
        [[self windowController] setShouldCascadeWindows: NO];

        NSWindow *window;
        window = [self window];
        [window setFrame: loadedWindowBounds display: YES];
    }

} // windowControllerDidLoadNib



- (NSData *) dataRepresentationOfType: (NSString *) aType
{
    NSArchiver *archiver;
    archiver = [[NSArchiver alloc] init];

    // save grid info(e)
    NSMutableDictionary *gridInfo;
    gridInfo = [[[NSMutableDictionary alloc] init] autorelease];

    [gridInfo setObject: [NSNumber numberWithFloat: [grid fabricWidth]]
                forKey: @"Fabric Width"];
    [gridInfo setObject: [NSNumber numberWithFloat: [grid fabricSpacing]]
                forKey: @"Fabric Spacing"];
    [gridInfo setObject: [grid backgroundColor]
                forKey: @"Background Color"];
    [gridInfo setObject: [grid threadColor]
                forKey: @"Thread Color"];
    [gridInfo setObject: [grid edgeColor]
                forKey: @"Edge Color"];
    [gridInfo setObject: [NSNumber numberWithFloat: [grid edgeWidth]]
                forKey: @"Edge Width"];
    [gridInfo setObject: [NSNumber numberWithBool: [grid flatGrid]]
                forKey: @"Flat Grid"];

    NSMutableDictionary *otherInfo;
    otherInfo = [[[NSMutableDictionary alloc] init] autorelease];
    [otherInfo setObject: [NSNumber numberWithBool: trackCenterOnly]
               forKey: @"Track Center Only"];
    [otherInfo setObject: [NSNumber numberWithBool: showBehindStitches]
                forKey: @"Show Behind Stitches"];

    BWGrid_Square printArea[2];
    printArea[0] = printAreaAnchor1;
    printArea[1] = printAreaAnchor2;

    NSValue *printAreaValue;
    printAreaValue = [NSValue valueWithBytes: printArea
                              objCType: @encode(BWGrid_Square[2])];

    NSRect windowRect;
    windowRect = [[self window] frame];
    NSValue *windowRectValue;
    windowRectValue = [NSValue valueWithBytes: &windowRect
                               objCType: @encode(NSRect)];
    
    BWStitch *templateStitch = [self templateStitch];

    NSDictionary *uberDictionary;
    uberDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                       gridInfo, @"Grid Info",
                                   otherInfo, @"Other Info",
                                   [stitches stitches], @"Stitches", 
                                   labelFont, @"Label Font",
                                   printAreaValue, @"Print Area",
                                   windowRectValue, @"Window Rect",
                                   templateStitch, @"Template Stitch",
                                   nil];

    NSData *data;
    data = [NSArchiver archivedDataWithRootObject: uberDictionary];

    return (data);

} // dataRepresentationOfType


- (BOOL) loadDataRepresentation: (NSData *) data  ofType: (NSString *) aType
{
    NSDictionary *uberDictionary;
    uberDictionary = [NSUnarchiver unarchiveObjectWithData: data];

    // first the grid attributes

    NSDictionary *gridAttributes;
    gridAttributes = [uberDictionary objectForKey: @"Grid Info"];

    grid = [[BWEvenweave alloc] init];
    
    NSNumber *number;
    NSColor *color;

    number = [gridAttributes objectForKey: @"Fabric Width"];
    [grid setFabricWidth: [number floatValue]];

    number = [gridAttributes objectForKey: @"Fabric Spacing"];
    [grid setFabricSpacing: [number floatValue]];

    color = [gridAttributes objectForKey: @"Background Color"];
    [grid setBackgroundColor: color];

    color = [gridAttributes objectForKey: @"Thread Color"];
    [grid setThreadColor: color];

    color = [gridAttributes objectForKey: @"Edge Color"];
    [grid setEdgeColor: color];

    number = [gridAttributes objectForKey: @"Edge Width"];
    [grid setEdgeWidth: [number floatValue]];

    number = [gridAttributes objectForKey: @"Flat Grid"];
    [grid setFlatGrid: [number boolValue]];


    // then other info(e)

    NSDictionary *otherInfo;
    otherInfo = [uberDictionary objectForKey: @"Other Info"];
    
    number = [otherInfo objectForKey: @"Show Behind Stitches"];
    showBehindStitches = [number boolValue];

    number = [otherInfo objectForKey: @"Track Center Only"];
    trackCenterOnly = [number boolValue];

    // then the stitches
    [stitchView setStitches: nil];

    NSArray *docStitches = [uberDictionary objectForKey: @"Stitches"];
    [stitches setStitches: docStitches];

    [stitchView setStitches: stitches];

    // now the font
    labelFont = [uberDictionary objectForKey: @"Label Font"];

    // and then the print area
    NSValue *value;
    value = [uberDictionary objectForKey: @"Print Area"];

    if (value == nil) {
        printAreaAnchor1.x = BWGrid_NO_SQUARE;
        printAreaAnchor1.y = BWGrid_NO_SQUARE;
        printAreaAnchor2.x = BWGrid_NO_SQUARE;
        printAreaAnchor2.y = BWGrid_NO_SQUARE;
    } else {
        BWGrid_Square printArea[2];

        [value getValue: printArea];
        printAreaAnchor1 = printArea[0];
        printAreaAnchor2 = printArea[1];
    }

    // window bounds
    value = [uberDictionary objectForKey: @"Window Rect"];
    if (value != nil) {
        [value getValue: &loadedWindowBounds];
    }

    // template stitch
    BWStitch *templateStitch;
    templateStitch = [uberDictionary objectForKey: @"Template Stitch"];

    if (!templateStitch) {
        templateStitch = [self makeTemplateStitch];
    }
    [templateStitches removeAllObjects];
    [templateStitches addObject: templateStitch];

    return (YES);

} // loadDataRepresentation


- (void) newStitchNotification: (NSNotification *) notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];

    BWStitch *stitch;
    stitch = [userInfo objectForKey: BWStitchView_NewStitchKey];
    
    if (stitch != nil) {
        stitch = [stitch copy];
        
        [self addStitch: stitch
              atPosition: [stitches count]];
    }

    [self updateChangeCount: NSChangeDone];
    
} //  newStitchNotification



- (void) postStitchSelectedNotification
{
    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];

    [center postNotificationName:
                BWBorkStitchDocument_StitchSelectionChangeNotification
            object: self
            userInfo: nil];

} // postStitchSelectedNotification



- (void) stitchSelectedNotification: (NSNotification *) notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];

    id payload;
    payload = [userInfo objectForKey: BWStitchView_SelectedStitchKey];

    if (payload == [NSNull null]) {
        [selectedStitches unselectAll];
    } else {
        [selectedStitches selectStitch: payload
                          unselectingOthers: NO];
    }
    
    [self postStitchSelectedNotification];

} // stitchSelectedNotification


- (void) stitchUnselectedNotification: (NSNotification *) notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];

    id payload;
    payload = [userInfo objectForKey: BWStitchView_SelectedStitchKey];

    [selectedStitches unselectStitch: payload];
    
    [self postStitchSelectedNotification];

} // stitchUnselectedNotification


- (void) printAreaChangeNotification: (NSNotification *) notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];

    NSValue *value;
    value = [userInfo valueForKey: BWStitchView_Anchor1SquareKey];
    [value getValue: &printAreaAnchor1];

    value = [userInfo valueForKey: BWStitchView_Anchor2SquareKey];
    [value getValue: &printAreaAnchor2];

    [self updateChangeCount: NSChangeDone];
    
} // printAreaChangeNotification




#define changeEverything(code) \
do { \
    NSEnumerator *enumerator;\
    enumerator = [stitches objectEnumerator]; \
    BWStitch *stitch; \
    while (stitch = [enumerator nextObject]) { \
        (code); \
    } \
} while (0)



// first-responder scaffolding
- (IBAction) borkstitchScaffolding: (id) sender
{
    int tag;
    tag = [sender tag];

    switch (tag) {
    case BWStitchView_Selection: {
        [stitchView setTrackMode: BWStitchView_Selection];
        break;
    }
    case BWStitchView_Drawing: {
        [stitchView setTrackMode: BWStitchView_Drawing];
        break;
    }
    case BWStitchView_PrintArea: {
        [stitchView setTrackMode: BWStitchView_PrintArea];
        [stitchView setShowPrintableArea: YES];
        [printableAreaCheckbox setState: NSOnState];
        break;
    }
    }

    [toolMatrix deselectAllCells];
    [toolMatrix selectCellWithTag: tag];

} // borkstitchScaffolding



- (IBAction) handleToolPalette: (id) sender
{
    id cell;
    cell = [sender selectedCell];

    int tag;
    tag = [cell tag];

    switch (tag) {
    case 0: {
        // selecting
        [stitchView setTrackMode: BWStitchView_Selection];
        break;
    }
    case 1: {
        // drawing
        [stitchView setTrackMode: BWStitchView_Drawing];
        break;
    }
    case 2: {
        // print area
        [stitchView setTrackMode: BWStitchView_PrintArea];
        [stitchView setShowPrintableArea: YES];
        [printableAreaCheckbox setState: NSOnState];
        break;
    }
    }

} // handleToolPalette


- (IBAction) frontBack: (id) sender
{
    NSIndexSet *selectedIndexes;
    selectedIndexes = [selectedStitches indexesForSelection];

    switch ([sender tag]) {
    case 0: { // front
        [self bringIndexesForward: selectedIndexes];
        break;
    }
    case 1: { // back
        [self sendIndexesBack: selectedIndexes];
        break;
    }
    case 2: { // flip
        [self flipStitchIndexes: selectedIndexes];
        break;
    }
    case 3: { // nuke
        [self deleteSelectedStitchesFromStitchView: stitchView];
        break;
    }
    }
    [stitchView setNeedsDisplay: YES];

} // frontBack


- (IBAction) front1: (id) sender
{
    NSLog (@"fronts");
} // front1


- (IBAction) back1: (id) sender
{
    NSLog (@"backs");
} // back1



- (IBAction) togglePrintableArea: (id) sender
{
    BOOL onOff;
    onOff = [sender state] == NSOnState;

    [stitchView setShowPrintableArea: onOff];

} // togglePrintableArea

// --------------------------------------------------
// printing and clipboard


// the user has chosen Copy.  Put a pdf on the clipboard of what we
// would have printed

- (IBAction) copy: (id) sender
{
    // make a place for the PDF data to be stored
    NSMutableData *data;
    data = [NSMutableData dataWithLength: 0];

    // making a printing view
    BWStitchView *printView;
    printView = [[BWStitchView alloc] initForPrintingWithGrid: grid
                                      stitches: stitches
                                      labelFont: labelFont
                                      printAreaAnchor1: printAreaAnchor1
                                      anchor2: printAreaAnchor2];

    [printView setPrintAreaAnchor1: printAreaAnchor1
                   anchor2: printAreaAnchor2];

    [printView setShowBehindStitches: showBehindStitches];

    // run the print operation, accumulating the data into the
    // NSData created above
    NSPrintOperation *printOperation;
    printOperation = [NSPrintOperation 
                         PDFOperationWithView: printView
                         insideRect: [printView bounds]
                         toData: data
                         printInfo: [self printInfo]];
    
    [printOperation runOperation];

    // now we have the PDF data.  stick it on the pasteboard
    NSPasteboard *pasteboard;
    pasteboard = [NSPasteboard  generalPasteboard];

    [pasteboard declareTypes: [NSArray arrayWithObject: NSPDFPboardType]
               owner: self];

    [pasteboard setData: data
                forType: NSPDFPboardType];

    [printView release];

} // copy



- (BWStitch *) selectedStitch
{
    return ([selectedStitches selectedStitch]);

} // selectedStitch


- (BWStitch *) targetStitch
{
    BWStitch *selectedStitch = [self selectedStitch];

    BWStitch *stitch;

    if (selectedStitch == nil) {
        stitch = [stitchView templateStitch];
    } else {
        stitch = selectedStitch;
    }

    return (stitch);

} // targetStitch

- (BWGrid *) grid
{
    return (grid);
} // grid


- (void) setNeedsDisplay: (BOOL) yesNo
{
    [stitchView setNeedsDisplay: yesNo];

} // setNeedsDisplay


- (NSFont *) labelFont
{
    return (labelFont);
} // labelFont



- (BWStitchArray *) stitches
{
    return (stitches);
} // stitches


- (BWStitch *) templateStitch
{
    return ([stitchView templateStitch]);
} // templateStitch


- (NSArray *) templateStitches
{
    return (templateStitches);

} // templateStitches


- (IBAction) toggleShowBehindStitches: (id) sender
{
    showBehindStitches = ([sender state] == NSOnState);

    [stitchView setShowBehindStitches: showBehindStitches];
    [self updateChangeCount: NSChangeDone];

} // toggleShowBehindStitches


// stitch view delegate method

- (void) stitchView: (BWStitchView *) view
 changedStartSquare: (BWGrid_Square) startSquare
         squarePart: (BWGrid_SquarePart) startPart
          endSquare: (BWGrid_Square) endSquare
         squarePart: (BWGrid_SquarePart) endPart
          forStitch: (BWStitch *) stitch
{
    [self changeStartSquare: startSquare
          squarePart: startPart
          endSquare: endSquare
          squarePart: endPart
          forStitch: stitch];

    [self setNeedsDisplay: YES];

} // changedStartSquare


- (void) stitchView: (BWStitchView *) view
  changedBendFactor: (float) bendFactor
   bendDisplacement: (float) bendDisplacement
          forStitch: (BWStitch *) stitch
{
    [self changeBendFactor: bendFactor
          displacement: bendDisplacement
          forStitch: stitch];

    [self setNeedsDisplay: YES];
          
} // changeBendFactor



- (void) changeFont: (id) sender
{
    NSFont *newFont;
    newFont = [sender convertFont: labelFont];

    [self setLabelFont: newFont];

    [self setNeedsDisplay: YES];

} // changeFont


- (void) unselectAll
{
    [stitchView unselectAll];
    [selectedStitches unselectAll];

} // unselectAll


- (IBAction) showInspector: (id) sender
{
    BWInspector *inspector;
    inspector = [BWInspector sharedInspector];

    BWInspectorPane *pane;

    pane = [[BWFabricInspectorPane alloc] init];
    [pane autorelease];
    [pane setDocument: self];
    [inspector addPane: pane];

    pane = [[BWLabelInspectorPane alloc] init];
    [pane autorelease];
    [pane setDocument: self];
    [inspector addPane: pane];

    pane = [[BWStitchInspectorPane alloc] init];
    [pane autorelease];
    [pane setDocument: self];
    [inspector addPane: pane];

    pane = [[BWShiftInspectorPane alloc] init];
    [pane autorelease];
    [pane setDocument: self];
    [inspector addPane: pane];

    // add paines
    [inspector show];

} // showInspector


- (void) updateToolUI
{
    BWInspector *inspector;
    inspector = [BWInspector sharedInspector];

    [inspector updatePaneUI];

} // updateToolUI


- (void) postNotification: (NSString *) notificationName
{
    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];
    
    [center postNotificationName: notificationName
            object: self];

} // postNotification


- (void) windowDidBecomeMain: (NSNotification *) notification
{
    [self postNotification: 
              BWBorkStitchDocument_DocumentActivateNotification];

} // windowDidBecomeMain


- (void) windowDidResignMain: (NSNotification *) notification
{
    [self postNotification: 
              BWBorkStitchDocument_DocumentDeactivateNotification];

} // windowDidResignMain


- (void) windowWillClose: (NSNotification *) notification
{
    [self postNotification: 
              BWBorkStitchDocument_DocumentDeactivateNotification];

} // windowDidClose



- (void) setTrackCenterOnly: (BOOL) tco
{
    trackCenterOnly = tco;
    [stitchView setTrackCenterOnly: tco];

} // setTrackCenterOnly


- (BOOL) trackCenterOnly
{
    return (trackCenterOnly);

} // trackCenterOnly


- (IBAction) selectAll: (id) sender
{
    [selectedStitches selectAll];

    [stitchView setTrackMode: BWStitchView_Selection];
    [stitchView setNeedsDisplay: YES];

    [toolMatrix deselectAllCells];
    [toolMatrix selectCellWithTag: BWStitchView_Selection];

    [self updateToolUI];

} // selectAll


- (void) deleteSelectedStitchesFromStitchView: (BWStitchView *) view
{
    if ([selectedStitches count] > 0) {
        NSIndexSet *selectedIndexes;
        selectedIndexes = [selectedStitches indexesForSelection];
        [self removeStitchesAtIndexes: selectedIndexes];
    }

} // deleteSelectedStitchesFromStitchView


- (void) stitchView: (BWStitchView *) view  shiftStitchesHorizontal: (int) horizontal
           vertical: (int) vertical
{
    NSIndexSet *indexes = nil; // default to move all
    if ([selectedStitches count] > 0) {
        indexes = [selectedStitches indexesForSelection];
    }

    [self shiftStitchesHorizontal: horizontal
          vertical: vertical
          indexes: indexes];

} // shiftStitchesHorizontal


- (BWStitchSelection *) selectedStitches
{
    return (selectedStitches);
} // selectedStitches

@end
